Overview

The motif analysis functions are now split across 5 focused files:

File Contents
R/motifs-data.R Shared constants: triad patterns (2 versions), MAN descriptions, theme
R/motifs.R Core: motif_census(), triad_census(), extract_triads(), get_edge_list()
R/motifs-extract.R extract_motifs() pipeline with print/plot methods
R/motifs-plot.R Visualization helpers (bar, heatmap, network, triad diagrams)
R/motifs-temporal.R extract_motifs_temporal(), triad_persistence() + methods

1. motif_census()

Statistical analysis of network motifs against a null model.

# Create a directed network with clear structure
set.seed(42)
mat <- matrix(0, 8, 8)
rownames(mat) <- colnames(mat) <- paste0("N", 1:8)

# Add structured edges (feed-forward loops, cycles, stars)
mat[1, 2] <- 1; mat[2, 3] <- 1; mat[1, 3] <- 1  # 030T: feed-forward
mat[4, 5] <- 1; mat[5, 6] <- 1; mat[6, 4] <- 1  # 030C: cycle
mat[7, 1] <- 1; mat[7, 2] <- 1; mat[7, 3] <- 1  # out-star from N7
mat[1, 8] <- 1; mat[2, 8] <- 1; mat[3, 8] <- 1  # in-star to N8
mat[4, 7] <- 1; mat[5, 8] <- 1; mat[6, 7] <- 1  # cross-connections

m <- motif_census(mat, directed = TRUE, n_random = 200, seed = 123)
print(m)
## Network Motif Analysis
## Size: 3-node motifs (directed)
## Null model: configuration (n=200)
## 
## Significant motifs:
##  motif count expected    z       p
##   111U     8      1.8 4.51 6.4e-06
## 
## Over-represented: 1 | Under-represented: 0

Bar plot (default)

plot(m, show_nonsig = TRUE)

Heatmap

plot(m, type = "heatmap", show_nonsig = TRUE)

Network visualization

plot(m, type = "network", show_nonsig = TRUE)


2. triad_census()

Simple count of the 16 MAN triad types.

tc <- cograph::triad_census(mat)
tc_df <- data.frame(
  Type = names(tc),
  Count = as.integer(tc),
  Description = .get_man_descriptions()[names(tc)]
)
tc_df <- tc_df[tc_df$Count > 0, ]
knitr::kable(tc_df, row.names = FALSE, caption = "Non-zero triad types")
Non-zero triad types
Type Count Description
012 31 Single edge
021D 2 Out-star
021U 3 In-star
021C 11 Chain
030T 8 Feed-forward
030C 1 Cycle

3. extract_triads()

Extract specific triads with node labels and edge weights.

# Use a weighted matrix
wmat <- matrix(0, 6, 6)
rownames(wmat) <- colnames(wmat) <- c("Plan", "Execute", "Monitor", "Adapt", "Review", "Reflect")
wmat["Plan", "Execute"] <- 15; wmat["Execute", "Monitor"] <- 12
wmat["Monitor", "Adapt"] <- 8;  wmat["Adapt", "Plan"] <- 10
wmat["Plan", "Monitor"] <- 6;   wmat["Execute", "Adapt"] <- 4
wmat["Review", "Reflect"] <- 9; wmat["Reflect", "Review"] <- 7
wmat["Monitor", "Review"] <- 5; wmat["Review", "Plan"] <- 3
wmat["Adapt", "Review"] <- 6;   wmat["Execute", "Review"] <- 2

triads <- extract_triads(wmat, min_total = 0)
knitr::kable(head(triads[order(-triads$total_weight), ], 10),
             row.names = FALSE,
             caption = "Top 10 triads by total weight")
Top 10 triads by total weight
A B C type weight_AB weight_BA weight_AC weight_CA weight_BC weight_CB total_weight
Plan Execute Monitor 030T 15 0 6 0 12 0 33
Plan Execute Adapt 030C 15 0 0 10 4 0 29
Plan Monitor Adapt 030C 6 0 0 10 8 0 24
Execute Monitor Adapt 030T 12 0 4 0 8 0 24
Adapt Review Reflect 111D 6 0 0 0 9 7 22
Monitor Review Reflect 111D 5 0 0 0 9 7 21
Plan Execute Review 030C 15 0 0 3 2 0 20
Plan Adapt Review 030T 0 10 0 3 6 0 19
Plan Review Reflect 111U 0 3 0 0 9 7 19
Execute Monitor Review 030T 12 0 2 0 5 0 19

Filter by motif type

# Only feed-forward loops
ff <- extract_triads(wmat, type = "030T", min_total = 0)
if (nrow(ff) > 0) {
  knitr::kable(ff, row.names = FALSE, caption = "Feed-forward loops (030T)")
} else {
  cat("No feed-forward loops found\n")
}
Feed-forward loops (030T)
A B C type weight_AB weight_BA weight_AC weight_CA weight_BC weight_CB total_weight
Plan Execute Monitor 030T 15 0 6 0 12 0 33
Plan Adapt Review 030T 0 10 0 3 6 0 19
Execute Monitor Adapt 030T 12 0 4 0 8 0 24
Execute Monitor Review 030T 12 0 2 0 5 0 19
Execute Adapt Review 030T 4 0 2 0 6 0 12
Monitor Adapt Review 030T 8 0 5 0 6 0 19
# Triads involving "Plan"
plan_triads <- extract_triads(wmat, involving = "Plan", min_total = 0)
knitr::kable(head(plan_triads, 10), row.names = FALSE,
             caption = "Triads involving 'Plan'")
Triads involving ‘Plan’
A B C type weight_AB weight_BA weight_AC weight_CA weight_BC weight_CB total_weight
Plan Execute Monitor 030T 15 0 6 0 12 0 33
Plan Execute Adapt 030C 15 0 0 10 4 0 29
Plan Execute Review 030C 15 0 0 3 2 0 20
Plan Execute Reflect 012 15 0 0 0 0 0 15
Plan Monitor Adapt 030C 6 0 0 10 8 0 24
Plan Monitor Review 030C 6 0 0 3 5 0 14
Plan Monitor Reflect 012 6 0 0 0 0 0 6
Plan Adapt Review 030T 0 10 0 3 6 0 19
Plan Adapt Reflect 012 0 10 0 0 0 0 10
Plan Review Reflect 111U 0 3 0 0 9 7 19

4. extract_motifs()

Detailed motif extraction with flexible filtering and optional significance testing.

From a matrix (aggregate level)

em <- extract_motifs(wmat, pattern = "all", min_transitions = 0)
print(em)
## Motif Analysis
## Pattern: all | Edge method: any
## Individuals: 1 | States: 6 | Total triads: 20
## 
## Type distribution:
## 
##  012 030T 030C 111D 111U 
##    6    6    4    3    1 
## 
## Top 20 triads:
##                          triad type observed
## 1     Adapt - Review - Reflect 111D        1
## 2    Execute - Adapt - Reflect  012        1
## 3     Execute - Adapt - Review 030T        1
## 4    Execute - Monitor - Adapt 030T        1
## 5  Execute - Monitor - Reflect  012        1
## 6   Execute - Monitor - Review 030T        1
## 7   Execute - Review - Reflect 111D        1
## 8    Monitor - Adapt - Reflect  012        1
## 9     Monitor - Adapt - Review 030T        1
## 10  Monitor - Review - Reflect 111D        1
## 11      Plan - Adapt - Reflect  012        1
## 12       Plan - Adapt - Review 030T        1
## 13      Plan - Execute - Adapt 030C        1
## 14    Plan - Execute - Monitor 030T        1
## 15    Plan - Execute - Reflect  012        1
## 16     Plan - Execute - Review 030C        1
## 17      Plan - Monitor - Adapt 030C        1
## 18    Plan - Monitor - Reflect  012        1
## 19     Plan - Monitor - Review 030C        1
## 20     Plan - Review - Reflect 111U        1

Triad network diagrams (default plot)

plot(em, n = 15, ncol = 5, node_size = 5, label_size = 6, title_size = 8)

Type distribution bar plot

plot(em, type = "types")

Abstract MAN patterns

plot(em, type = "patterns")

With significance testing

if (requireNamespace("tna", quietly = TRUE)) {
  library(tna)
  Mod <- tna(group_regulation)

  em_sig <- extract_motifs(Mod, pattern = "triangle",
                           significance = TRUE, n_perm = 50,
                           top = 20, seed = 42)
  print(em_sig)
} else {
  cat("tna package not available - skipping significance test demo\n")
}
## Motif Analysis
## Pattern: triangle | Edge method: any
## Individuals: 2000 | States: 9 | Total triads: 20
## 
## Type distribution:
## 
## 120C 030C 030T  210 120U 120D  300 
## 1482 1054  620  581  190  178   79 
## 
## Top 20 triads:
##                                triad type observed expected     z sig
## 1     cohesion - consensus - emotion 120C      261    104.1 18.89 ***
## 2    consensus - discuss - synthesis 120C      176     54.7 18.29 ***
## 3   consensus - coregulate - discuss 120C      330    177.2 13.83 ***
## 4        adapt - discuss - synthesis 030T       24      5.5  9.20 ***
## 5        adapt - consensus - discuss 120C       88     39.9  8.29 ***
## 6         consensus - emotion - plan 120C      436    339.4  6.66 ***
## 7   consensus - coregulate - emotion 030C      165    113.5  5.17 ***
## 8      consensus - coregulate - plan 120C      301    235.6  5.13 ***
## 9   consensus - coregulate - monitor 030C       73     43.8  4.99 ***
## 10   cohesion - coregulate - emotion 030C       52     28.1  4.77 ***
## 11    coregulate - discuss - monitor 030C       50     27.5  3.95 ***
## 12         cohesion - emotion - plan 030C      122     94.4  3.94 ***
## 13        consensus - monitor - plan 120C      187    149.4  3.33 ***
## 14     consensus - discuss - monitor 120C      127     98.9  3.16  **
## 15 cohesion - consensus - coregulate 030C       79     58.1  3.13  **
## 16      adapt - cohesion - consensus 030C       17      9.8  2.19   *
## 17     adapt - consensus - synthesis 030T       10      5.9  1.75    
## 18  coregulate - discuss - synthesis 030C       19     13.6  1.56    
## 19     consensus - discuss - emotion 120C      238    219.1  1.47    
## 20       discuss - emotion - monitor 030C       39     32.7  1.03
plot(em_sig, n = 20, ncol = 5, node_size = 5, label_size = 6)

plot(em_sig, type = "significance")


5. extract_motifs_temporal()

Track motif evolution across time windows.

if (requireNamespace("tna", quietly = TRUE)) {
  data <- group_regulation

  tm <- extract_motifs_temporal(
    data,
    window_size = 5,
    step = 2,
    pattern = "all",
    min_transitions = 5
  )
  print(tm)
} else {
  cat("tna package not available - skipping temporal demo\n")
}
## Temporal Motif Analysis
## Windows: 6 | Pattern: all
## Window size: 5 | Step: 2
## 
## Total occurrences by type:
##  type count
##   300   404
##   210    89
##  120C     5
##  120D     4
##  030T     1
##  120U     1

Heatmap

plot(tm, type = "heatmap")


6. triad_persistence()

Analyze which triads persist, emerge, or fade across time.

By specific triads

pers <- triad_persistence(tm, by = "triad", min_windows = 1)
print(pers)
## Triad Persistence Analysis (by triad)
## Windows: 6 | Triads tracked: 84
## 
## Status distribution:
##   Persistent: 84 | Transient: 0 | Emerging: 0 | Fading: 0 | Sporadic: 0
## 
## Top 15 triads by persistence:
##                             triad type persistence     windows     status
##     consensus - discuss - emotion  300           1 1,2,3,4,5,6 persistent
##  consensus - coregulate - discuss  300           1 1,2,3,4,5,6 persistent
##        consensus - emotion - plan  300           1 1,2,3,4,5,6 persistent
##        consensus - monitor - plan  300           1 1,2,3,4,5,6 persistent
##         cohesion - emotion - plan  300           1 1,2,3,4,5,6 persistent
##      cohesion - discuss - emotion  300           1 1,2,3,4,5,6 persistent
##     consensus - coregulate - plan  300           1 1,2,3,4,5,6 persistent
##  consensus - coregulate - monitor  300           1 1,2,3,4,5,6 persistent
##     consensus - emotion - monitor  300           1 1,2,3,4,5,6 persistent
##          emotion - monitor - plan  300           1 1,2,3,4,5,6 persistent
##    cohesion - consensus - emotion  300           1 1,2,3,4,5,6 persistent
##       cohesion - consensus - plan  300           1 1,2,3,4,5,6 persistent
##  consensus - coregulate - emotion  300           1 1,2,3,4,5,6 persistent
##    coregulate - discuss - emotion  300           1 1,2,3,4,5,6 persistent
##    cohesion - consensus - discuss  300           1 1,2,3,4,5,6 persistent
plot(pers, type = "heatmap", top_n = 25)

plot(pers, type = "timeline", top_n = 20)

plot(pers, type = "status")

By MAN type

pers_type <- triad_persistence(tm, by = "type")
print(pers_type)
## Triad Persistence Analysis (by type)
## Windows: 6 | MAN types tracked: 6
## 
## Status distribution:
##   Persistent: 3 | Transient: 2 | Emerging: 1 | Fading: 0 | Sporadic: 0
## 
## Top 6 triads by persistence:
##  triad type persistence     windows     status
##    300  300   1.0000000 1,2,3,4,5,6 persistent
##    210  210   1.0000000 1,2,3,4,5,6 persistent
##   120D 120D   0.6666667     1,3,5,6 persistent
##   120C 120C   0.3333333         5,6   emerging
##   030T 030T   0.1666667           4  transient
##   120U 120U   0.1666667           1  transient
plot(pers_type, type = "heatmap")

plot(pers_type, type = "heatmap", normalize = TRUE)


File Structure Summary

After refactoring, here are the line counts:

File Lines
R/motifs-data.R 127
R/motifs.R 780
R/motifs-extract.R 616
R/motifs-plot.R 435
R/motifs-temporal.R 1058
Total 3016

Original: 3,220 lines in a single file After refactoring: 3016 lines across 5 files (6% reduction)